前兩篇寫了 slice 是什麼,以及創建 slice 的基本語法。
這篇就來寫 slice 的參數傳遞以及 append 到底做了什麼。
func pass(arr []int) {
arr[0] = 4
}
a := []int{1, 2, 3}
pass(a)
看了這段程式,我們都知道 slice 的第一個元素被改為 4 了。
這時候就會有人說 slice 是 pass by refernece,但是 go 只有 pass by value,還記得 Day1 理解 golang slice 用法及原理 I 所說的 slice header 嗎,其實這個函式傳遞的是 slice header 的副本。slice 的第一個元素被更動,是因為兩個 slice header 指向了共同的 underying array。
看圖
再看一段程式
func pass(arr []int) {
arr = []int{4, 5, 6}
}
a := []int{1, 2, 3}
pass(a)
arr 的更改並不會影響 a 的內容,因為兩個 slice header 指向不同的 underlying array
如果想讓 a 指向不同的 underyling array,應該要傳送 slice header 的地址。
func pass(arr *[]int) {
arr = []int{4, 5, 6}
}
a := []int{1, 2, 3}
pass(&a)
先看一些兩個基本語法
s := []int{1, 2, 3}
s = append(s, 1)
s1 := []int{1, 2, 3}
s2 := []int{4, 5, 6}
s1 = append(s1, s2...)
簡單的一個問題, slice 在 append 過後還指向同一個 underlying array 嗎? 還是分配了一塊新的?
在回答問題之前,先了解一下 append 的實作概念,Reference 1 給了一個很好的概念範例,這段範例給出了 append 設計的想法 (注意:這只是設計的概念)。(如果不熟 copy 怎麼使用,自己 google 一下囉)
func Append(slice []int, elements ...int) []int {
n := len(slice)
total := len(slice) + len(elements)
if total > cap(slice) {
// Reallocate. Grow to 1.5 times the new size, so we can still grow.
newSize := total*3/2 + 1
newSlice := make([]int, total, newSize)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[:total]
copy(slice[n:], elements)
return slice
}
還記得 Day2 理解 golang slice 用法及原理 II 礦泉水的例子嗎,假設想把兩瓶水裝成一瓶,如果本來瓶子容量夠大,把另一瓶水裝進來就好。如果裝不下,那就去找更大的瓶子,把這兩瓶水裝進去囉。至於為什麼乘 3 除 2 ,那不重要,這邊想表達的意思就是要找比原來兩個 slice 加起來還要大的容量。
懂了之後,回答原本的問題就很簡單了,答案是不一定。